/* 
*  Copyright (C) 2009-2010 WCG Project
*
*  This program is free software; you can redistribute it and/or modify it
*  under the terms of the GNU General Public License as published by the
*  Free Software Foundation; either version 2 of the License, or (at
*  your option) any later version.
*
*  This program is distributed in the hope that it will be useful, but
*  WITHOUT ANY WARRANTY; without even the implied warranty of
*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
*  General Public License for more details.
*
*  You should have received a copy of the GNU General Public License
*  along with this program; if not, write to the Free Software Foundation,
*  Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*  In addition, as a special exception, the author gives permission to
*  link the code of this program with the Half-Life Game Engine ("HL
*  Engine") and Modified Game Libraries ("MODs") developed by Valve,
*  L.L.C ("Valve"). You must obey the GNU General Public License in all
*  respects for all of the code used other than the HL Engine and MODs
*  from Valve. If you modify this file, you may extend this exception
*  to your version of the file, but you are not obligated to do so. If
*  you do not wish to do so, delete this exception statement from your
*  version.
*/


new Float:vSavedPlayerOrigin[33][3]


new const p_frostbolt[5]				= {180, 160, 140, 120, 100}				// Frostbolt
new const Float:p_frostarmor[5]			= {0.02, 0.04, 0.06, 0.08, 0.10}		// Frost Armor
new const p_farmorslow[5]				= {190, 180, 170, 160, 150}				// Frost Armor slowing values
new const Float:p_blastwave[5]			= {0.12, 0.14, 0.16, 0.18, 0.20}		// Blast Wave


MAGE_InitSkills()
{
	SM_InitSkill( SKILL_FROSTBOLT, CLASS_MAGE, SKILL_TYPE_TRAINABLE, sizeof(p_frostbolt), 1, 3 )
	SM_InitSkill( SKILL_FROSTARMOR, CLASS_MAGE, SKILL_TYPE_TRAINABLE, 5 )
	SM_InitSkill( SKILL_BLASTWAVE, CLASS_MAGE, SKILL_TYPE_TRAINABLE, sizeof(p_blastwave), 15, 5 )
	SM_InitSkill( SKILL_FROSTNOVA, CLASS_MAGE, SKILL_TYPE_TRAINABLE, 5, 30 )
	SM_InitSkill( ABILITY_POLYCHICKEN, CLASS_MAGE, SKILL_TYPE_ABILITY, 5, 20, 10 )
	SM_InitSkill( ULTIMATE_BLINK, CLASS_MAGE, SKILL_TYPE_ULTIMATE, 5, 10, 15 )
}


//------------------------------------
// MAGE_TriggerAbility
//
//------------------------------------
MAGE_TriggerAbility( id, iSkillID )
{
	if( iSkillID == ABILITY_POLYCHICKEN )
	{
		PLAYER_CastSpell( id, ABILITY_POLYCHICKEN, CAST_SEARCH, "_SPELL_PolyChicken", _, _, CAST_SEARCH_OPPOSITE_TEAM )
	}
}


//------------------------------------
// MAGE_TriggerUltimate
//
//------------------------------------
MAGE_TriggerUltimate( id, iSkillID )
{
	if( iSkillID == ULTIMATE_BLINK )
	{
		new param[1]
		param[0] = id
		PLAYER_CastSpell( id, ULTIMATE_BLINK, CAST_NORMAL, "_SPELL_Blink", param, 1  )
	}
}





//------------------------------------
// MAGE_SkillsOffensive
//
//------------------------------------
public MAGE_SkillsOffensive( iAttacker, iVictim, iWeapon, &iDamage, iDamageType )
{
	static iSkillLevel

	if( !is_user_alive( iVictim ) )
		return

	// Frostbolt
	iSkillLevel = SM_GetSkillLevel( iAttacker, SKILL_FROSTBOLT )
	if( iSkillLevel > 0 && (iDamageType & (DMG_BULLET|DMG_SLASH)))
	{
		if( !SM_IsOnCooldown( iAttacker, SKILL_FROSTBOLT ) && !PlayerInfo[iVictim][PI_Slowed] )
		{
			if( random_float( 0.0, 1.0 ) <= (0.40 * float(iSkillLevel) / float(g_SkillMaxLevel[SKILL_FROSTBOLT]) ) )
			{
				new param[3]
				param[0] = iAttacker
				param[1] = iVictim
				param[2] = iSkillLevel
				PLAYER_CastSpell( iAttacker, SKILL_FROSTBOLT, CAST_PASSIVE, "_SPELL_Frostbolt", param, 3 )
				return
			}
		}
	}


	// Blast Wave
	iSkillLevel = SM_GetSkillLevel( iAttacker, SKILL_BLASTWAVE )
	if( iSkillLevel > 0 && (iDamageType & (DMG_BULLET|DMG_SLASH)) && !SM_IsOnCooldown( iAttacker, SKILL_BLASTWAVE ) )
	{
		if( random_float( 0.0, 1.0 ) <= p_blastwave[iSkillLevel-1] )
		{
			new param[3]
			param[0] = iAttacker
			param[1] = iVictim
			param[2] = iSkillLevel
			PLAYER_CastSpell( iAttacker, SKILL_BLASTWAVE, CAST_PASSIVE, "_SPELL_BlastWave", param, 3 )
			return
		}
	}
}



//------------------------------------
// MAGE_SkillsDefensive
//
//------------------------------------
public MAGE_SkillsDefensive( iAttacker, iVictim, &iDamage, iWeapon, iDamageType )
{
	static iSkillLevel

	// Frost Armor
	iSkillLevel = SM_GetSkillLevel( iVictim, SKILL_FROSTARMOR )
	if( iSkillLevel > 0 && (iDamageType & (DMG_BULLET|DMG_SLASH|DMG_HEGRENADE)) )
		MAGE_FrostArmor( iSkillLevel, iVictim, iAttacker, iDamage )

}




//------------------------------------
// MAGE_FrostArmor
//
//------------------------------------
MAGE_FrostArmor( iSkillLevel, iVictim, iAttacker, &iDamage )
{
	if( !is_user_alive(iVictim) )
		return

	new iTemp = floatround( float( iDamage ) * p_frostarmor[iSkillLevel-1] )
//	new Float:fChance = 0.30 * iSkillLevel / g_SkillMaxLevel[SKILL_FROSTARMOR]

	iDamage -= iTemp

	SHARED_Glow( iVictim, COLOR_RGB( 0, ( 2 * iTemp ), ( 3 * iTemp ) ) )

	if( is_user_alive( iAttacker ) /*&& fChance <= random_float(0.0, 1.0) */)
	{
		if( !SHARED_IsPlayerSlowed( iAttacker ) )
		{
			if( !PLAYER_CheckMagicHit( iVictim, iAttacker ) )
				return

			PlayerInfo[iAttacker][PI_Slowed] = p_farmorslow[iSkillLevel-1]
			PLAYER_UpdateSpeed( iAttacker )
			SHARED_Glow( iAttacker, COLOR_RGB( 100, 128, 150 ) )
			EFFECT_ScreenFade( iAttacker, 0.25, 0.25, COLOR_RGBA(0, 128, 255, 128) )
			set_task( 2.0, "SHARED_ResetMaxSpeed", TASK_RESETSPEED + iAttacker )
		}
	}
}




//--------------------------------------------------------------------------------------------------------------------------
// Blast Wave

#define BLASTWAVE_DAMAGE		30

//------------------------------------
// _SPELL_BlastWave
//
// PLAYER_CastSpell callback
//		param[0] = Attacker ID
//		param[1] = Victim ID
//------------------------------------
public _SPELL_BlastWave( const param[] )
{
	static Float:vOrigin[3], Float:vTargetOrigin[3], Float:vDir[3]

	new iCasterID	= param[0]
	new iVictimID	= param[1]

	if( !is_user_alive( iVictimID ) )
		return

	if( !PLAYER_CheckMagicHit( iCasterID, iVictimID ) )
	{
		client_print( iCasterID, print_chat, "Your Blast Wave was resisted by %s.", PlayerInfo[iVictimID][PI_PlayerName] )
		client_print( iVictimID, print_chat, "You resisted %s's Blast Wave.", PlayerInfo[iCasterID][PI_PlayerName] )
		client_cmd( iCasterID, "spk %s", g_szSounds[SOUND_DISPELL] )
		return
	}


	pev( iVictimID, pev_origin, vOrigin )
	vOrigin[2] -= 16.0
	
	emit_sound( iVictimID, CHAN_STATIC, g_szSounds[SOUND_FIRENOVA], VOL_NORM, ATTN_NORM, 0, PITCH_NORM )

	PLAYER_MagicDamage( iVictimID, iCasterID, BLASTWAVE_DAMAGE, CSW_BLASTWAVE, DMG_BURN )

	static vEffectOrigin[3], vAxisOrigin[3]
	vEffectOrigin[0] = floatround( vOrigin[0] )
	vEffectOrigin[1] = floatround( vOrigin[1] )
	vEffectOrigin[2] = floatround( vOrigin[2] )

	vAxisOrigin[0] = vEffectOrigin[0]
	vAxisOrigin[1] = vEffectOrigin[1]
	vAxisOrigin[2] = vEffectOrigin[2] + 300

	Create_TE_BEAMCYLINDER( vEffectOrigin, vEffectOrigin, vAxisOrigin, g_spr_shockwave, 0, 0, 10, 20, 0, 220, 188, 64, 128, 0 )

	SHARED_Glow( iVictimID, COLOR_RGB( 150, 128, 32 ) )

	new iTargetTeam = PlayerInfo[iVictimID][PI_TeamID]

	new param[5]
	param[0] = _:vOrigin[0]
	param[1] = _:vOrigin[1]
	param[2] = _:vOrigin[2]
	param[3] = 2
	set_task( 0.2, "_BlastWaveFX", TASK_EFFECT, param, 4 )


	new iTargetID, Float:fDistance, Float:fXDist, Float:fYDist, Float:fZDist, Float:fInvLen, Float:fRand
	
	for( new i = 0 ; i < g_NumTeamPlayers[iTargetTeam] ; i++ )
	{
		iTargetID = g_SortedTeams[iTargetTeam][i]

		if( !is_user_alive(iTargetID) || iTargetID == iVictimID )
			continue
		
		pev( iTargetID, pev_origin, vTargetOrigin )
		
		fYDist = vTargetOrigin[2] - vOrigin[2]
		
		if( fYDist > 72.0 || fYDist < -72.0 )
			continue

		fXDist = vTargetOrigin[0] - vOrigin[0]
		fZDist = vTargetOrigin[1] - vOrigin[1]
		fDistance = floatsqroot( fXDist*fXDist + fZDist*fZDist )

		if( fDistance > 300.0 )
			continue

		if( !PLAYER_CheckMagicHit( iCasterID, iTargetID ) )
		{
			client_print( iTargetID, print_chat, "You resisted %s's Blast Wave.", PlayerInfo[iCasterID][PI_PlayerName] )
			continue
		}

		fInvLen = 1.0 / fDistance
		fRand = random_float( 250.0, 400.0 )
		vDir[0] = fXDist * fInvLen * fRand
		vDir[1] = fZDist * fInvLen * fRand
		vDir[2] = random_float( 250.0, 500.0 )

		param[0] = iCasterID
		param[1] = iTargetID
		param[2] = _:vDir[0]
		param[3] = _:vDir[1]
		param[4] = _:vDir[2]
		set_task( fDistance * 0.003, "_BlastWaveImpact", TASK_ALIVE_EFFECT + iTargetID, param, 5 )
	}
}


//------------------------------------
// _BlastWaveImpact
//
//------------------------------------
public _BlastWaveImpact( param[] )
{
	static Float:velocity[3]

	new iCasterID = param[0]
	new iTargetID = param[1]
	
	if( !is_user_alive(iTargetID) || !is_user_connected(iCasterID) )
		return
	
	PLAYER_MagicDamage( iTargetID, iCasterID, BLASTWAVE_DAMAGE, CSW_BLASTWAVE, DMG_BURN )

	if( PlayerInfo[iTargetID][PI_IsImmobilized] )
		return

	velocity[0] = Float:param[2]
	velocity[1] = Float:param[3]
	velocity[2] = Float:param[4]

	set_pev( iTargetID, pev_velocity, velocity )

	if( !SHARED_IsPlayerSlowed( iTargetID ) )
	{
		PlayerInfo[iTargetID][PI_Slowed] = 150
		PLAYER_UpdateSpeed( iTargetID )
		SHARED_Glow( iTargetID, COLOR_RGB( 150, 128, 32 ) )
		EFFECT_ScreenFade( iTargetID, 0.25, 0.25, COLOR_RGBA(255, 164, 64, 128) )
		set_task( 3.0, "SHARED_ResetMaxSpeed", TASK_RESETSPEED + iTargetID )
	}
}



//------------------------------------
// _BlastWaveFX
//
//------------------------------------
public _BlastWaveFX( param[] )
{
	static Float:vOrigin[3]
	
	vOrigin[0] = Float:param[0]
	vOrigin[1] = Float:param[1]
	vOrigin[2] = Float:param[2]
	new iCounter = param[3]

	new Float:fRadius = float( iCounter * 30 )
	new Float:fRadCounter = M_PI2 / (8.0 * (fRadius / 300.0))
	new Float:fRandMax = fRadCounter * 0.5
	new Float:fRandMin = -fRandMax
	new Float:theta

	static vDest[3]
	vDest[2] = floatround( vOrigin[2] ) - 16
	
	for( new Float:i = fRadCounter ; i < M_PI2 ; i+=fRadCounter )
	{
		theta = i + random_float( fRandMin, fRandMax )

		vDest[0] = floatround( vOrigin[0] + floatcos(theta) * fRadius )
		vDest[1] = floatround( vOrigin[1] + floatsin(theta) * fRadius )
		Create_TE_EXPLOSION( vDest, vDest, g_spr_explode1, 10, 15, TE_EXPLFLAG_NOSOUND )
	}

	iCounter++
	if( iCounter > 10 )
		return
	
	param[3] = iCounter
	set_task( 0.1, "_BlastWaveFX", TASK_EFFECT, param, 4 )
}



//--------------------------------------------------------------------------------------------------------------------------
// Frost Nova

new g_NovaEntity[33]


//------------------------------------
// MAGE_FrostNova
//
//------------------------------------
MAGE_FrostNova( ent )
{
	static Float:vOrigin[3], Float:vTargetOrigin[3], vEffectOrigin[3], vAxisOrigin[3]
	static vLightOrigin[3], Float:fTimer

	new iTargetID, Float:fDistance
	
	new id = pev( ent, pev_owner )

	new iSkillLevel = pev( ent, pev_iuser2 )
	if( iSkillLevel <= 0 || iSkillLevel > g_SkillMaxLevel[SKILL_FROSTNOVA] )
		return

	if( !is_user_connected( id ) )
		return

	pev( ent, pev_origin, vOrigin )

	vEffectOrigin[0] = floatround( vOrigin[0] )
	vEffectOrigin[1] = floatround( vOrigin[1] )
	vEffectOrigin[2] = floatround( vOrigin[2] )  + 16

	vAxisOrigin[0] = vEffectOrigin[0]
	vAxisOrigin[1] = vEffectOrigin[1]
	vAxisOrigin[2] = vEffectOrigin[2] + 600

	Create_TE_BEAMCYLINDER( vEffectOrigin, vEffectOrigin, vAxisOrigin, g_spr_shockwave, 0, 0, 6, 20, 0, 188, 220, 255, 255, 0 )
	
	emit_sound( ent, CHAN_STATIC, g_szSounds[SOUND_FROSTNOVA], VOL_NORM, ATTN_NORM, 0, PITCH_NORM )

	new iTargetTeam = ( PlayerInfo[id][PI_TeamID] == TEAM_CT ) ? TEAM_T : TEAM_CT

	for( new i = 0 ; i < g_NumTeamPlayers[iTargetTeam] ; i++ )
	{
		iTargetID = g_SortedTeams[iTargetTeam][i]

		if( !is_user_alive( iTargetID ) )
			continue
		
		pev( iTargetID, pev_origin, vTargetOrigin )
		
		fDistance = get_distance_f( vOrigin, vTargetOrigin )
		if( fDistance < 450.0 )
		{
			if( !PLAYER_CheckMagicHit( id, iTargetID ) )
			{
				client_print( iTargetID, print_chat, "You resisted %s's Frost Nova.", PlayerInfo[id][PI_PlayerName] )
				continue
			}

			if( PlayerInfo[iTargetID][PI_IsImmobilized] )
				continue
				
			if( PlayerInfo[iTargetID][PI_Stealthed] )
				_RemoveStealth( iTargetID )

			if( PlayerInfo[iTargetID][PI_CanRender] )
				fm_set_user_rendering( iTargetID, kRenderFxGlowShell, 32, 164, 255, kRenderNormal, 18 )

			PlayerInfo[iTargetID][PI_IsImmobilized] = true
			PlayerInfo[iTargetID][PI_CanRender] = false

			PLAYER_UpdateSpeed( iTargetID )
			PLAYER_UpdateGravity( iTargetID )

/*			vLightOrigin[0] = floatround( vTargetOrigin[0] )
			vLightOrigin[1] = floatround( vTargetOrigin[1] )
			vLightOrigin[2] = floatround( vTargetOrigin[2] ) + 75
			Create_TE_ELIGHT( id, vLightOrigin, 100, 164, 255, 200, iSkillLevel, 0 )

			// Thanks to Avalanche for the following wonderful piece of frostgrenade code
			new entity = engfunc( EngFunc_CreateNamedEntity, engfunc( EngFunc_AllocString, "info_target" ) )
			engfunc( EngFunc_SetSize, entity, Float:{-8.0,-8.0,-4.0}, Float:{8.0,8.0,4.0} )
			engfunc( EngFunc_SetModel, entity, "models/wcg/frostnova.mdl" )

			static Float:vAngle[3]
			vAngle[1] = random_float(0.0, 360.0 )
			set_pev( entity, pev_angles, vAngle )

			static Float:vMins[3]
			pev( iTargetID, pev_mins, vMins )
			vTargetOrigin[2] += vMins[2]
			engfunc( EngFunc_SetOrigin, entity, vTargetOrigin )

			set_pev( entity, pev_renderfx, kRenderFxNone )
			set_pev( entity, pev_rendercolor, {64.0, 172.0, 255.0} )
			set_pev( entity, pev_rendermode, kRenderTransColor )
			set_pev( entity, pev_renderamt, 150.0 )

			g_NovaEntity[iTargetID] = entity
*/
			fTimer = random_float( 2.0, 3.0 + float( iSkillLevel ) )

			set_task( fTimer, "_RemoveFrostNova", TASK_REMOVEFROSTNOVA + iTargetID )
		}
	}
}


//------------------------------------
// _RemoveFrostNova
//
//------------------------------------
public _RemoveFrostNova( id ) 
{
	id = GetTaskID( id, TASK_REMOVEFROSTNOVA )

	PlayerInfo[id][PI_IsImmobilized] = false

	if( PlayerInfo[id][PI_Stealthed] == 0 )
		PlayerInfo[id][PI_CanRender] = true

/*	if( pev_valid(g_NovaEntity[id]) )
	{
		set_pev( g_NovaEntity[id], pev_flags, pev(g_NovaEntity[id], pev_flags) | FL_KILLME )
		g_NovaEntity[id] = 0
	}
*/
	if( !GameInfo[GI_FreezeTime] )
	{
		PLAYER_UpdateSpeed( id )
		PLAYER_UpdateGravity( id )
		PLAYER_UpdateInvisibility( id )
	}
}


//--------------------------------------------------------------------------------------------------------------------------
// Frostbolt

//------------------------------------
// _SPELL_Frostbolt
//
// PLAYER_CastSpell callback
//		param[0] = Caster ID
//		param[1] = Victim ID
//		param[2] = Skill level
//------------------------------------
public _SPELL_Frostbolt( const param[] )
{
	new iAttacker = param[0]
	new iVictim = param[1]
	new iSkillLevel = param[2]

	if( !is_user_alive( iAttacker ) || !is_user_alive( iVictim ) )
		return

	emit_sound( iVictim, CHAN_STATIC, g_szSounds[SOUND_BLIZZARDIMPACT], VOL_NORM, ATTN_NORM, 0, PITCH_NORM )

	static vAttackerOrigin[3], vVictimOrigin[3]
	get_user_origin( iAttacker, vAttackerOrigin )
	get_user_origin( iVictim, vVictimOrigin )
	Create_TE_SPRITETRAIL( vAttackerOrigin, vVictimOrigin, g_spr_xspark2, 40, 0, 2, 5, 5 )

	if( !PLAYER_CheckMagicHit( iAttacker, iVictim ) )
	{
		client_print( iAttacker, print_chat, "Your Frostbolt was resisted by %s.", PlayerInfo[iVictim][PI_PlayerName] )
		client_print( iVictim, print_chat, "You resisted %s's Frostbolt.", PlayerInfo[iAttacker][PI_PlayerName] )
		client_cmd( iAttacker, "spk %s", g_szSounds[SOUND_DISPELL] )
		return
	}

	SHARED_Glow( iVictim, COLOR_RGB( 100, 128, 150 ) )
	EFFECT_ScreenFade( iVictim, 0.25, 0.25, COLOR_RGBA(0, 128, 255, 128) )

	if( !SHARED_IsPlayerSlowed( iVictim ) )
	{
		PlayerInfo[iVictim][PI_Slowed] = p_frostbolt[iSkillLevel-1]
		PLAYER_UpdateSpeed( iVictim )
		set_task( 3.0, "SHARED_ResetMaxSpeed", TASK_RESETSPEED + iVictim )
	}

	_Frostbolt_Smoke( iVictim )
	set_task( 0.2, "_Frostbolt_Smoke", TASK_EFFECT + iVictim, _, _, "a", 3 )
}


//------------------------------------
// _Frostbolt_Smoke
//
//------------------------------------
public _Frostbolt_Smoke( id )
{
	id = GetTaskID( id, TASK_EFFECT )

	static vOrigin[3], vDest[3]
	get_user_origin( id, vOrigin )

	for( new i = 0 ; i < 2 ; i++ )
	{
		vDest[0] = vOrigin[0] + random_num(-16, 16)
		vDest[1] = vOrigin[1] + random_num(-16, 16)
		vDest[2] = vOrigin[2] + random_num(-32, 16)
		Create_TE_EXPLOSION( vDest, vDest, g_spr_stmbal1, 15, 15, TE_EXPLFLAG_NODLIGHTS|TE_EXPLFLAG_NOSOUND|TE_EXPLFLAG_NOPARTICLES )
	}
}





//--------------------------------------------------------------------------------------------------------------------------
// Blink

#define BLINK_LENGTH	50.0

//------------------------------------
// _SPELL_Blink
//
// PLAYER_CastSpell callback
//		param[0] = Caster ID
//------------------------------------
public _SPELL_Blink( const param[] )
{
	static const Float:vUp[3] = { 0.0, 0.0, 1.0 }

	static Float:vStartOrigin[3], Float:vEndOrigin[3], Float:vEndGround[3], Float:vAngle[3]
	static Float:vDirection[3], Float:vPlaneNormal[3]

	new i, hTrace, Float:fraction, Float:fDot

	new id = param[0]

	if( !is_user_alive(id) )
		return

	// Get player origin and rotation angle
	pev( id, pev_origin, vSavedPlayerOrigin[id] )
	pev( id, pev_angles, vAngle )

	new Float:theta = xs_deg2rad(vAngle[1])

	xs_vec_set( vDirection, floatcos(theta) * BLINK_LENGTH, floatsin(theta) * BLINK_LENGTH, 0.0 )

	// Find the ground at the start origin
	xs_vec_copy( vSavedPlayerOrigin[id], vStartOrigin )
	xs_vec_copy( vSavedPlayerOrigin[id], vEndOrigin )
	vEndOrigin[2] -= 8192.0

	engfunc( EngFunc_TraceLine, vStartOrigin, vEndOrigin, IGNORE_MONSTERS, FM_NULLENT, hTrace )
	get_tr2( hTrace, TR_vecEndPos, vStartOrigin )
	get_tr2( hTrace, TR_flFraction, fraction )
	if( fraction == 1.0 )
	{
		SM_SetCooldown( id, ULTIMATE_BLINK, 5 )
		PlayerInfo[id][PI_fMana] += float(PlayerInfo[id][PI_LastManaCost])
		PlayerInfo[id][PI_Mana] = clamp( floatround(PlayerInfo[id][PI_fMana]), 0, PlayerInfo[id][PI_MaxMana] )
		WCG_StatusText( id, 0, HUD_AUTO, "%L", id, "TELEPORT_FAILED_BAD_DESTINATION" )
		client_cmd( id, "spk %s", g_szSounds[SOUND_ERROR] )
		return
	}
	vStartOrigin[2] += 48.0

	for( i = 0 ; i < 10 ; i ++ )
	{
		fDot = 1.0

		// Find the end origin
		xs_vec_copy( vStartOrigin, vEndOrigin )
		xs_vec_add( vEndOrigin, vDirection, vEndOrigin )

		// Trace to see if we hit something along the way
		engfunc( EngFunc_TraceLine, vStartOrigin, vEndOrigin, IGNORE_MONSTERS, FM_NULLENT, hTrace )
		get_tr2( hTrace, TR_vecEndPos, vEndOrigin )
		get_tr2( hTrace, TR_flFraction, fraction )
		if( fraction < 1.0 )
		{
			get_tr2( hTrace, TR_vecPlaneNormal, vPlaneNormal )
			fDot = xs_vec_dot( vPlaneNormal, vUp )
			xs_vec_mul_scalar( vPlaneNormal, 16.0, vPlaneNormal )
			xs_vec_add( vEndOrigin, vPlaneNormal, vEndOrigin )
		}
	
		// Find the ground at the end origin
		xs_vec_copy( vEndOrigin, vEndGround )
		vEndGround[2] -= 8192.0
	
		engfunc( EngFunc_TraceLine, vEndOrigin, vEndGround, IGNORE_MONSTERS, FM_NULLENT, hTrace )
		get_tr2( hTrace, TR_vecEndPos, vEndOrigin )
		get_tr2( hTrace, TR_flFraction, fraction )
		if( fraction == 1.0 )
		{
			SM_SetCooldown( id, ULTIMATE_BLINK, 5 )
			PlayerInfo[id][PI_fMana] += float(PlayerInfo[id][PI_LastManaCost])
			PlayerInfo[id][PI_Mana] = clamp( floatround(PlayerInfo[id][PI_fMana]), 0, PlayerInfo[id][PI_MaxMana] )
			WCG_StatusText( id, 0, HUD_AUTO, "%L", id, "TELEPORT_FAILED_BAD_DESTINATION" )
			client_cmd( id, "spk %s", g_szSounds[SOUND_ERROR] )
			return
		}
		vEndOrigin[2] += 48.0

		// Diagnostic line
//		static vStart[3], vEnd[3]
//		FVecIVec( vStartOrigin, vStart )
//		FVecIVec( vEndOrigin, vEnd )
//		Create_TE_BEAMPOINTS( vStart, vEnd, g_spr_zbeam1, 0, 0, 100 )

		if( fDot <= 0.7 )
			break

		xs_vec_copy( vEndOrigin, vStartOrigin )
	}


	// Validate destination
	new bFoundSpot = false
	xs_vec_set( vDirection, floatcos(theta) * 16.0, floatsin(theta) * 16.0, 0.0 )

	// Trace backwards and try to find a open spot
	for( i = 0 ; i < 10 ; i ++ )
	{
		engfunc( EngFunc_TraceHull, vEndOrigin, vEndOrigin, DONT_IGNORE_MONSTERS, HULL_HUMAN, id, hTrace )
		if( !get_tr2( hTrace, TR_StartSolid ) )
		{
			bFoundSpot = true
			break
		}

		xs_vec_sub( vEndOrigin, vDirection, vEndOrigin )

		// Find the ground at the new end origin
		xs_vec_copy( vEndOrigin, vEndGround )
		vEndGround[2] -= 8192.0
		vEndOrigin[2] += 18.0
	
		engfunc( EngFunc_TraceLine, vEndOrigin, vEndGround, IGNORE_MONSTERS, FM_NULLENT, hTrace )
		get_tr2( hTrace, TR_flFraction, fraction )
		if( fraction == 1.0 )
			continue

		get_tr2( hTrace, TR_vecEndPos, vEndOrigin )
		vEndOrigin[2] += 48.0
	}

	if( !bFoundSpot )
	{
		SM_SetCooldown( id, ULTIMATE_BLINK, 5 )
		PlayerInfo[id][PI_fMana] += float(PlayerInfo[id][PI_LastManaCost])
		PlayerInfo[id][PI_Mana] = clamp( floatround(PlayerInfo[id][PI_fMana]), 0, PlayerInfo[id][PI_MaxMana] )
		WCG_StatusText( id, 0, HUD_AUTO, "%L", id, "TELEPORT_FAILED_BAD_DESTINATION" )
		client_cmd( id, "spk %s", g_szSounds[SOUND_ERROR] )
		return
	}

	client_cmd( id, "-use" )

	static vPlayerOrigin[3]
	FVecIVec( vSavedPlayerOrigin[id], vPlayerOrigin )

	Create_TE_TELEPORT( vPlayerOrigin )

	// Move the player
	fm_entity_set_origin( id, vEndOrigin )

	emit_sound( id, CHAN_STATIC, g_szSounds[SOUND_TELEPORT], VOL_NORM, ATTN_NORM, 0, PITCH_NORM )
	EFFECT_ScreenFade( id, 0.25, 0.25, COLOR_RGBA(0, 128, 255, 128) )

	// Sprays white bubbles everywhere
	static vDestOrigin1[3], vDestOrigin2[3]
	FVecIVec( vEndOrigin, vDestOrigin1 )
	FVecIVec( vEndOrigin, vDestOrigin2 )
	vDestOrigin1[2] += 40

	Create_TE_SPRITETRAIL( vDestOrigin1, vDestOrigin2, g_spr_blueflare2, 30, 10, 1, 50, 10 )

	PlayerInfo[id][PI_IsStunned] = false
	PlayerInfo[id][PI_IsImmobilized] = false
	PLAYER_UpdateSpeed( id )
	PLAYER_UpdateGravity( id )
}


//--------------------------------------------------------------------------------------------------------------------------
// Polymorph Chicken

new poly_pevBodyValue[33]
new poly_selectedWeapon[33]
new poly_szViewModel[33][72]
new poly_szWeaponModel[33][72]
new Float:poly_sizeMin[33][3]
new Float:poly_sizeMax[33][3]
new Float:poly_viewofs[33][3]
new poly_armor[33]
new CsArmorType:poly_armortype[33]

//------------------------------------
// _SPELL_PolyChicken
//
// PLAYER_CastSpell callback
//		param[0] = Caster ID
//		param[1] = Victim ID
//------------------------------------
public _SPELL_PolyChicken( param[] )
{
	new iCasterID = param[0]
	new iVictimID = param[1]

	if( !is_user_alive(iVictimID) )
		return

	if( !PLAYER_CheckMagicHit( iCasterID, iVictimID ) )
	{
		client_print( iCasterID, print_chat, "Your Polymorph was resisted by %s.", PlayerInfo[iVictimID][PI_PlayerName] )
		client_print( iVictimID, print_chat, "You resisted %s's Polymorph.", PlayerInfo[iCasterID][PI_PlayerName] )
		client_cmd( iCasterID, "spk %s", g_szSounds[SOUND_DISPELL] )
		return
	}

	if( PlayerInfo[iVictimID][PI_IsPolymorphed] )
	{
		client_print( iCasterID, print_chat, "%s Your Polymorph had no effect", g_MODclient )
		emit_sound( iCasterID, CHAN_STATIC, g_szSounds[SOUND_DISPELL], VOL_NORM, ATTN_NORM, 0, PITCH_NORM )
		return
	}

	emit_sound( iVictimID, CHAN_STATIC, g_szSounds[SOUND_POLYMORPHCHICKEN], VOL_NORM, ATTN_NORM, 0, PITCH_NORM )

	poly_pevBodyValue[iVictimID] = pev( iVictimID, pev_body )
	pev( iVictimID, pev_viewmodel2, poly_szViewModel[iVictimID], 71 )
	pev( iVictimID, pev_weaponmodel2, poly_szWeaponModel[iVictimID], 71 )
	pev( iVictimID, pev_mins, poly_sizeMin[iVictimID] )
	pev( iVictimID, pev_maxs, poly_sizeMax[iVictimID] )
	pev( iVictimID, pev_view_ofs, poly_viewofs[iVictimID] )

	poly_armor[iVictimID] = cs_get_user_armor( iVictimID, poly_armortype[iVictimID] )

	cs_set_user_armor( iVictimID, 0, CS_ARMOR_NONE )

	static vOrigin[3], vOrigin2[3]
	get_user_origin( iVictimID, vOrigin )
	vOrigin2[0] = vOrigin[0]
	vOrigin2[1] = vOrigin[1]
	vOrigin2[2] = vOrigin[2] + 40
	Create_TE_TELEPORT( vOrigin )
	Create_TE_SPRITETRAIL( vOrigin, vOrigin2, g_spr_blueflare2, 30, 10, 1, 50, 10 )

	new ammo, clip
	poly_selectedWeapon[iVictimID] = get_user_weapon( iVictimID, clip, ammo )

	engclient_cmd( iVictimID, "weapon_knife" )

	set_pev( iVictimID, pev_body, 0 )
	set_pev( iVictimID, pev_viewmodel2, "" )
	set_pev( iVictimID, pev_weaponmodel2, "" )

	cs_set_user_model( iVictimID, "chicken" )

	engfunc( EngFunc_SetSize, iVictimID, {0.0, 0.0, 0.0}, {32.0, 32.0, 36.0} )
	set_pev( iVictimID, pev_view_ofs, {0.0, 0.0, -10.0} )

	PlayerInfo[iVictimID][PI_IsPolymorphed] = true
	WCG_StatusText( iVictimID, 0, HUD_CASTING, "You are Polymorphed" )

	PLAYER_UpdateSpeed( iVictimID )
	PLAYER_UpdateGravity( iVictimID )
	
	set_task( random_float( 1.0, 5.0 ), "_RemovePolyChicken", TASK_REMOVEPOLYCHICKEN + iVictimID )
}


//------------------------------------
// _RemovePolyChicken
//
//------------------------------------
public _RemovePolyChicken( id )
{
	id = GetTaskID( id, TASK_REMOVEPOLYCHICKEN )

	remove_task( TASK_REMOVEPOLYCHICKEN + id )

	if( !PlayerInfo[id][PI_IsPolymorphed] )
		return

	PlayerInfo[id][PI_IsPolymorphed] = false

	cs_reset_user_model( id )
	
	if( GameInfo[GI_FreezeTime] )
		return

	set_pev( id, pev_body, poly_pevBodyValue[id] )
	set_pev( id, pev_viewmodel2, poly_szViewModel[id] )
	set_pev( id, pev_weaponmodel2, poly_szWeaponModel[id] )
	set_pev( id, pev_view_ofs, poly_viewofs[id] )

	if( user_has_weapon( id, poly_selectedWeapon[id] ) )
	{
		static weaponName[32]
		get_weaponname( poly_selectedWeapon[id], weaponName, 31 )
		engclient_cmd( id, weaponName )
	}

	engfunc( EngFunc_SetSize, id, poly_sizeMin[id], poly_sizeMax[id] )

	cs_set_user_armor( id, poly_armor[id], poly_armortype[id] )

	PLAYER_UpdateSpeed( id )
	PLAYER_UpdateGravity( id )
}
